#include "stdafx.h"

#include "CmdLineProc.h"

CmdLineProc::CmdLineProc()
{
	mUsage = "";
}

void CmdLineProc::AddOption(const string & name, const string & description, bool mandatory, bool allowMultiple, const string & defaultVal)
{
	if (!mandatory && defaultVal == "")
	{
		throw new string("If option is not mandatory, default value must be specified");
	}

	Option o;
	o.name = name;
	o.description = description;
	o.mandatory = mandatory;
	o.allowMultiple = allowMultiple;
	o.defaultValue = defaultVal;

	mDefinedOptions.push_back(o);
}

void CmdLineProc::AddFlag(const string & name, const string & description, bool mandatory)
{
	Flag f;
	f.name = name;
	f.description = description;
	f.mandatory = mandatory;

	mDefinedFlags.push_back(f);
}

string CmdLineProc::GetValue(const string & name)
{
	multimap<string, string>::size_type cnt = mOptions.count(name);
	if (cnt < 1)
	{
		return "";
	}

	multimap<string, string>::iterator iter;
	iter = mOptions.find(name);
	return iter->second;
}

void CmdLineProc::GetValue(const string & name, vector<string> & options)
{
	options.clear();
	multimap<string, string>::iterator iter;

	multimap<string, string>::size_type size = mOptions.size();
	iter = mOptions.begin();
	for (multimap<string, string>::size_type i = 0; i < size; i++)
	{	
		if (iter->first == name)
		{
			options.push_back(iter->second);
		}
		iter++;
	}
}

bool CmdLineProc::GetFlag(const string & name)
{
	map<string, bool>::iterator iter;

	if (mFlags.count(name) < 1)
	{
		return false;
	}

	iter = mFlags.find(name);
	return iter->second;
}

bool CmdLineProc::IsSupplied(const string & name)
{	
	if (mOptions.count(name) > 0)
		return true;
	else if (mFlags.count(name) > 0)
		return true;

	return false;
}

vector<string> & CmdLineProc::Parse(const string & cmdLine)
{
	mParsedValues.clear();

	vector<string>::size_type pos;
	vector<string>::size_type len;
	vector<string>::size_type start = 0;

	do
	{
		pos = cmdLine.find(" ", start);
		len = pos - start;
		string sub = cmdLine.substr(start, len);

		if (sub != "")
		{
			mParsedValues.push_back(sub);
		}
		start += (len + 1);
	} while(pos != string::npos);

	return mParsedValues;
}

void CmdLineProc::LoadCommandString(const string & cmdLineString)
{
	vector<string> arguments = Parse(cmdLineString);
	LoadCommandString(arguments);
}

void CmdLineProc::LoadCommandString(int argc, char *const argv[])
{
	vector<string> arguments;
	for (int i = 1; i < argc; i++)
	{
		arguments.push_back(string(argv[i]));
	}
	LoadCommandString(arguments);
}

void CmdLineProc::LoadCommandString(vector<string> & arguments)
{
	// create usage
	mUsage = "";
	mUsage += "\nArguments:\n";
	for (vector<Option>::size_type i = 0; i < mDefinedOptions.size(); i++)
	{
		if (mDefinedOptions[i].mandatory)
			mUsage += "\t" + mDefinedOptions[i].name + "=x - " + mDefinedOptions[i].description + "\n";
		else
			mUsage += "\t[" + mDefinedOptions[i].name + "] =x - " + mDefinedOptions[i].description + "\n";
	}

	mUsage += "\nFlags:\n";
	for (vector<Flag>::size_type i = 0; i < mDefinedFlags.size(); i++)
	{
		if (mDefinedFlags[i].mandatory)
			mUsage += "\t" + mDefinedFlags[i].name + " - " + mDefinedFlags[i].description + "\n";
		else
			mUsage += "\t[" + mDefinedFlags[i].name + "] - " + mDefinedFlags[i].description + "\n";
	}

	// parse input arguments
	for (vector<string>::size_type i = 0; i < arguments.size(); i++)
	{
		string v = arguments[i];
		vector<string>::size_type pe = v.find("=");
		if (pe != string::npos)	// option, not flag
		{
			string name = v.substr(0, pe);
			string option = v.substr(pe+1);
			if (option.size() < 1)
			{
				throw new string("Option [" + name + "] is missing a value.");
			}

			// is it a valid option
			bool valid = false;
			for (vector<Option>::size_type j = 0; j < mDefinedOptions.size(); j++)
			{
				if (mDefinedOptions[j].name == name)
				{
					valid = true;
					mOptions.insert(make_pair(name, option));
					break;
				}
			}
			if (!valid)
			{
				throw new string("Unknown option specified: " + name + ".");
			}
		}
		else
		{
			string name = v;

			bool valid = false;
			for (vector<Flag>::size_type j = 0; j < mDefinedFlags.size(); j++)
			{
				if (mDefinedFlags[j].name == name)
				{	
					if (mFlags.count(name) > 0)
					{
						throw new string("Duplicate flag [" + name + "].");
					}
					valid = true;
					mFlags.insert(make_pair(v, true));				
					break;
				}
			}
			if (!valid)
			{
				throw new string("Unknown flag specified: " + name + ".");
			}
			
		}
	}

	// add defaults when options are not mandatory
	for (vector<Option>::size_type j = 0; j < mDefinedOptions.size(); j++)
	{
		// check if option in mOptions and if not, and if it is not mandatory, add the default value to mOptions
		multimap<string, string>::size_type c = mOptions.count(mDefinedOptions[j].name);
		if (c < 1 && !mDefinedOptions[j].mandatory)
		{
			mOptions.insert(make_pair(mDefinedOptions[j].name, mDefinedOptions[j].defaultValue));
		}
	}


	// verify all mandatory options and flags are present and that we don't have duplicate when not supposed
	string errorMessage = "";
	bool hasError = false;
	for (vector<Option>::size_type j = 0; j < mDefinedOptions.size(); j++)
	{
		multimap<string, string>::size_type i = mOptions.count(mDefinedOptions[j].name);
		if (i < 1 && mDefinedOptions[j].mandatory)
		{				
			errorMessage += "Mandatory option [" + mDefinedOptions[j].name + "] missing. ";
			hasError = true;			
		}
		if (i > 1 && !mDefinedOptions[j].allowMultiple)
		{
			errorMessage += "Duplicate option not allowed [" + mDefinedOptions[j].name + "]. ";
			hasError = true;			
		}
	}	

	for (vector<Flag>::size_type j = 0; j < mDefinedFlags.size(); j++)
	{
		map<string, bool>::size_type i = mFlags.count(mDefinedFlags[j].name);
		if (i < 1 && mDefinedFlags[j].mandatory)
		{
			errorMessage += "Mandatory flag [" + mDefinedFlags[j].name + "] missing. ";
			hasError = true;
		}		
	}

	if (hasError)
	{
		throw new string(errorMessage);
	}
	
}

string & CmdLineProc::GetUsage()
{
	return mUsage;
}
